using System;
using System.Data;
using System.Configuration;
using System.IO;
using EnvDTE;
using System.Collections.Generic;
using System.Reflection;

namespace Microsoft.VSPowerToys.ResourceRefactor.Common
{

    /// <summary>
    /// Basic implementation supporting all C# projects.
    /// </summary>
    /// <remarks>This implementation supports resx files using ResXFileCodeGenerator custom tool and
    /// has a very low priority so other implementations can be used instead for specific projects.</remarks>
    public class GenericCSharpExtractResourceAction : IExtractResourceAction
    {

        #region IExtractResourceAction Members

        /// <summary>
        /// This action has a very low priority since it is generic to all C# projects.
        /// </summary>
        public virtual int Priority
        {
            get { return 10; }
        }

        /// <summary>
        /// Gets the default relative path for resource file.
        /// </summary>
        public virtual string DefaultResourceFilePath
        {
            get { return String.Empty; }
        }

        /// <summary>
        /// Supports all C# files and C# projects
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public virtual bool QuerySupportForProject(EnvDTE.ProjectItem item)
        {
            if (item == null) return false;
            return 
                item.Document.Language.Equals("CSharp") &&
                ExtensibilityMethods.GetProjectType(item.ContainingProject) == ProjectType.CSharp;
        }

        /// <summary>
        /// This method will be used for filtering resource files displayed to user
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        /// <returns>true if resource file is valid and should be displayed to user</returns>
        public virtual bool IsValidResourceFile(EnvDTE.ProjectItem item)
        {
            if (item == null) return false;
            try
            {
                if (item.Properties.Item("Extension").Value.ToString().Equals(".resx", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (item.Properties.Item("ItemType").Value.Equals("EmbeddedResource") &&
                    (item.Properties.Item("CustomTool").Value.Equals("ResXFileCodeGenerator")))
                    {
                            return true;
                    }
                }
                return false;
            }
            catch (ArgumentException)
            {
                // This can happen if item does not contain the properties we are looking for.
                return false;
            }
        }

        /// <summary>
        /// Determines the namespace of the provided resource file
        /// </summary>
        /// <param name="file">Reference to the resource file</param>
        /// <returns>Namespace to be used to access the resource file</returns>
        protected virtual string GetNamespacePrefix(ResourceFile file)
        {
            string namespacePrefix = String.Empty;
            try
            {
                namespacePrefix = file.CustomToolNamespace;
                if (String.IsNullOrEmpty(namespacePrefix) && file.FileNamespace != null)
                {
                    namespacePrefix = file.FileNamespace;
                }
                else
                {
                    namespacePrefix = file.Item.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
                }
                namespacePrefix += ".";
            }
            catch (ArgumentException)
            {
                namespacePrefix = String.Empty;
            }
            return namespacePrefix;
        }

        /// <summary>
        /// Returns the code reference to resource specified in the parameters
        /// </summary>
        /// <param name="file">Resource file containing the resource</param>
        /// <param name="resourceName">Name of the resource</param>
        /// <returns>a piece of code that would reference to the resource provided</returns>
        /// <remarks>This method does not verify if resource actually exists</remarks>
        public virtual string GetResourceReference(ResourceFile file, string resourceName)
        {
            if (file == null)
            {
                throw new ArgumentNullException("file");
            }
            if (String.IsNullOrEmpty(resourceName))
            {
                throw new ArgumentException(Strings.InvalidResourceName, "resourceName");
            }
            string namespacePrefix = this.GetNamespacePrefix(file);    
            string reference = namespacePrefix + Path.GetFileNameWithoutExtension(file.DisplayName).Replace(' ', '_') + "." + resourceName.Replace(' ', '_');
            return reference;
        }

        /// <summary>
        /// This method should update properties on a recently created resource file so that it
        /// is correctly supported by the same instance of IExtractResourceAction
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        public virtual void UpdateResourceFileProperties(ProjectItem item)
        {
            if (item != null)
            {
                item.Properties.Item("CustomTool").Value = "ResXFileCodeGenerator";
                item.Properties.Item("ItemType").Value = "EmbeddedResource";
            }
        }

        #endregion
    }

    /// <summary>
    /// Basic implementation supporting all VB projects.
    /// </summary>
    /// <remarks>This implementation supports resx files using ResXFileCodeGenerator custom tool and
    /// has a very low priority so other implementations can be used instead for specific projects.</remarks>
    public class GenericVBExtractResourceAction : IExtractResourceAction
    {

        #region IExtractResourceAction Members

        /// <summary>
        /// This action has a very low priority since it is generic to all VB.Net projects.
        /// </summary>
        public virtual int Priority
        {
            get { return 10; }
        }

        /// <summary>
        /// Gets the default relative path for resource file.
        /// </summary>
        public virtual string DefaultResourceFilePath
        {
            get { return String.Empty; }
        }

        /// <summary>
        /// Supports all VB files and VB projects
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public virtual bool QuerySupportForProject(EnvDTE.ProjectItem item)
        {
            return
                item != null && item.Document.Language.Equals("Basic") &&
                ExtensibilityMethods.GetProjectType(item.ContainingProject) == ProjectType.VB;
        }

        /// <summary>
        /// This method will be used for filtering resource files displayed to user
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        /// <returns>true if resource file is valid and should be displayed to user</returns>
        public virtual bool IsValidResourceFile(EnvDTE.ProjectItem item)
        {
            if (item == null) return false;
            try
            {
                if (item.Properties.Item("Extension").Value.ToString().Equals(".resx", StringComparison.InvariantCultureIgnoreCase))
                {
                    
                        if (item.Properties.Item("ItemType").Value.Equals("EmbeddedResource") &&
                        (item.Properties.Item("CustomTool").Value.Equals("ResXFileCodeGenerator") ||
                         item.Properties.Item("CustomTool").Value.Equals("VbMyResourcesResXFileCodeGenerator")))
                        {
                            return true;
                        }
                }
                return false;
            }
            catch (ArgumentException)
            {
                // This can happen if item does not contain the properties we are looking for.
                return false;
            }
        }

        /// <summary>
        /// Returns the code reference to resource specified in the parameters
        /// </summary>
        /// <param name="file">Resource file containing the resource</param>
        /// <param name="resourceName">Name of the resource</param>
        /// <returns>a piece of code that would reference to the resource provided</returns>
        /// <remarks>This method does not verify if resource actually exists</remarks>
        public string GetResourceReference(ResourceFile file, string resourceName)
        {
            if (file == null)
            {
                throw new ArgumentNullException("file");
            }
            if (String.IsNullOrEmpty(resourceName))
            {
                throw new ArgumentException(Strings.InvalidResourceName, "resourceName");
            }
            string namespacePrefix = GetNamespacePrefix(file);
            if (!file.IsDefaultResXFile())
            {
                namespacePrefix += Path.GetFileNameWithoutExtension(file.DisplayName).Replace(' ', '_') + ".";
            }
            string reference = namespacePrefix + resourceName.Replace(' ', '_');
            return reference;
        }

        #endregion

        /// <summary>
        /// Determines the namespace of the provided resource file
        /// </summary>
        /// <param name="file">Reference to the resource file</param>
        /// <returns>Namespace to be used to access the resource file</returns>
        protected virtual string GetNamespacePrefix(ResourceFile file)
        {
            if (file == null)
            {
                throw new ArgumentNullException("file");
            }
            string namespacePrefix = file.CustomToolNamespace;
            if (String.IsNullOrEmpty(namespacePrefix)) 
            {
                namespacePrefix = file.FileNamespace;
            }
            if (!String.IsNullOrEmpty(namespacePrefix))
            {
                namespacePrefix += ".";
            }
            return namespacePrefix;
        }

        #region IExtractResourceAction Members

        /// <summary>
        /// This method should update properties on a recently created resource file so that it
        /// is correctly supported by the same instance of IExtractResourceAction
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        public virtual void UpdateResourceFileProperties(ProjectItem item)
        {
            if (item != null)
            {
                item.Properties.Item("CustomTool").Value = "ResXFileCodeGenerator";
                item.Properties.Item("ItemType").Value = "EmbeddedResource";
                //For VB project, we need to set namespace property as well
                item.Properties.Item("CustomToolNamespace").Value = "My.Resources";
            }
        }
        #endregion
    }

    /// <summary>
    /// Implementation supporting C# file and website projects.
    /// </summary>
    /// <remarks>This implementation is used when string is in a C# file which is contained by a website project</remarks>
    public class WebsiteCSharpExtractResourceAction : GenericCSharpExtractResourceAction
    {
        #region IExtractResourceAction Members

        /// <summary>
        /// Priority of the action. If there are multiple actions supporting the same item, action with
        /// the highest priority will be selected
        /// </summary>
        public override int Priority
        {
            get { return 50; }
        }

        /// <summary>
        /// Gets the default relative path for resource file.
        /// </summary>
        public override string DefaultResourceFilePath
        {
            get
            {
                return "App_GlobalResources";
            }
        }

        /// <summary>
        /// Queries if this action supports the provided project item and its containing project
        /// </summary>
        /// <param name="item">Project item to query support for</param>
        /// <returns></returns>
        public override bool QuerySupportForProject(ProjectItem item)
        {
            return
                item != null && item.Document.Language.Equals("CSharp") &&
                ExtensibilityMethods.GetProjectType(item.ContainingProject) == ProjectType.WebProject;
        }

        /// <summary>
        /// This method will be used for filtering resource files displayed to user
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        /// <returns>true if resource file is valid and should be displayed to user</returns>
        public override bool IsValidResourceFile(ProjectItem item)
        {
            return CheckResourceFileForWebSites(item);
        }

        #endregion

        /// <summary>
        /// Determines the namespace of the provided resource file
        /// </summary>
        /// <param name="file">Reference to the resource file</param>
        /// <returns>Namespace to be used to access the resource file</returns>
        protected override string GetNamespacePrefix(ResourceFile file)
        {
            return "Resources.";
        }

        /// <summary>
        /// Checks if provided item is a valid resource file for website projects
        /// </summary>
        /// <param name="item"></param>
        /// <returns>true if item is a valid resource file</returns>
        internal static bool CheckResourceFileForWebSites(ProjectItem item) 
        {
            try
            {
                if (item.Properties.Item("Extension").Value.ToString().Equals(".resx", StringComparison.InvariantCultureIgnoreCase))
                {
                    return Path.GetDirectoryName(item.Properties.Item("RelativeURL").Value.ToString()).Equals("App_GlobalResources");
                }
                return false;
            }
            catch (ArgumentException)
            {
                // This can happen if item does not contain the properties we are looking for.
                return false;
            }
        }

        /// <summary>
        /// This method should update properties on a recently created resource file so that it
        /// is correctly supported by the same instance of IExtractResourceAction
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        public override void UpdateResourceFileProperties(ProjectItem item)
        {
        }
    }

    /// <summary>
    /// Implementation supporting VB file and website projects.
    /// </summary>
    /// <remarks>This implementation is used when string is in a VB file which is contained by a website project</remarks>
    public class WebsiteVBExtractResourceAction : GenericVBExtractResourceAction
    {
        #region IExtractResourceAction Members

        /// <summary>
        /// Priority of the action. If there are multiple actions supporting the same item, action with
        /// the highest priority will be selected
        /// </summary>
        public override int Priority
        {
            get { return 50; }
        }

        /// <summary>
        /// Gets the default relative path for resource file.
        /// </summary>
        public override string DefaultResourceFilePath
        {
            get
            {
                return "App_GlobalResources";
            }
        }

        /// <summary>
        /// Queries if this action supports the provided project item and its containing project
        /// </summary>
        /// <param name="item">Project item to query support for</param>
        /// <returns></returns>
        public override bool QuerySupportForProject(ProjectItem item)
        {
            return
                item != null && item.Document.Language.Equals("Basic") &&
                ExtensibilityMethods.GetProjectType(item.ContainingProject) == ProjectType.WebProject;
        }

        /// <summary>
        /// This method will be used for filtering resource files displayed to user
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        /// <returns>true if resource file is valid and should be displayed to user</returns>
        public override bool IsValidResourceFile(ProjectItem item)
        {
            return WebsiteCSharpExtractResourceAction.CheckResourceFileForWebSites(item);
        }

        #endregion

        /// <summary>
        /// Determines the namespace of the provided resource file
        /// </summary>
        /// <param name="file">Reference to the resource file</param>
        /// <returns>Namespace to be used to access the resource file</returns>
        protected override string GetNamespacePrefix(ResourceFile file)
        {
            return "Resources.";
        }

        /// <summary>
        /// This method should update properties on a recently created resource file so that it
        /// is correctly supported by the same instance of IExtractResourceAction
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        public override void UpdateResourceFileProperties(ProjectItem item)
        {
        }
    }

    /// <summary>
    /// Implementation supporting C# file and web application projects.
    /// </summary>
    public class WebApplicationCSharpExtractResourceAction : GenericCSharpExtractResourceAction
    {
        #region IExtractResourceAction Members

        /// <summary>
        /// Priority of the action. If there are multiple actions supporting the same item, action with
        /// the highest priority will be selected
        /// </summary>
        public override int Priority
        {
            get { return 50; }
        }

        /// <summary>
        /// Gets the default relative path for resource file.
        /// </summary>
        public override string DefaultResourceFilePath
        {
            get
            {
                return "App_GlobalResources";
            }
        }

        /// <summary>
        /// Queries if this action supports the provided project item and its containing project
        /// </summary>
        /// <param name="item">Project item to query support for</param>
        /// <returns></returns>
        public override bool QuerySupportForProject(ProjectItem item)
        {
            if (item == null) return false;
            bool returnValue = base.QuerySupportForProject(item);
            bool extenderFound = false;
            if (returnValue)
            {
                string[] names = (string[])(item.ContainingProject.ExtenderNames);
                foreach (string name in names)
                {
                    if (name.Equals("WebApplication"))
                    {
                        extenderFound = true;
                        break;
                    }
                }
            }
            return returnValue && extenderFound;
        }

        /// <summary>
        /// This method will be used for filtering resource files displayed to user
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        /// <returns>true if resource file is valid and should be displayed to user</returns>
        public override bool IsValidResourceFile(ProjectItem item)
        {
            return base.IsValidResourceFile(item) || IsValidContentResourceFile(item);
        }

        #endregion

        /// <summary>
        /// Determines the namespace of the provided resource file
        /// </summary>
        /// <param name="file">Reference to the resource file</param>
        /// <returns>Namespace to be used to access the resource file</returns>
        protected override string GetNamespacePrefix(ResourceFile file)
        {
            if (file.CustomToolName.Equals("GlobalResourceProxyGenerator"))
            {
                return "Resources.";
            }
            else
            {
                return base.GetNamespacePrefix(file);
            }
        }

        /// <summary>
        /// This method should update properties on a recently created resource file so that it
        /// is correctly supported by the same instance of IExtractResourceAction
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        public override void UpdateResourceFileProperties(ProjectItem item)
        {
            if (item != null)
            {
                string relPath = GetRelativePathForItem(item);
                if (relPath.Equals("App_GlobalResources"))
                {
                    item.Properties.Item("CustomTool").Value = "GlobalResourceProxyGenerator";
                    item.Properties.Item("ItemType").Value = "Content";
                }
                else
                {
                    base.UpdateResourceFileProperties(item);
                }
            }
        }

        /// <summary>
        /// Checks if resource file is a valid content resource file used in web application projects
        /// These resource files must be located in App_GlobalResources directory
        /// </summary>
        /// <returns></returns>
        public static bool IsValidContentResourceFile(ProjectItem item)
        {
            if (item == null) return false;
            try
            {
                if (item.Properties.Item("Extension").Value.ToString().Equals(".resx", StringComparison.InvariantCultureIgnoreCase))
                {
                    if (item.Properties.Item("ItemType").Value.Equals("Content") &&
                        item.Properties.Item("CustomTool").Value.Equals("GlobalResourceProxyGenerator"))
                    {
                        string relPath = GetRelativePathForItem(item);
                        return relPath.Equals("App_GlobalResources");
                    }
                }
                return false;
            }
            catch (ArgumentException)
            {
                // This can happen if item does not contain the properties we are looking for.
                return false;
            }
        }

        /// <summary>
        /// Gets the relative path of the item in the project
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        public static string GetRelativePathForItem(ProjectItem item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            string itemFullPath = Path.GetDirectoryName(item.get_FileNames(1));
            string projectFullPath = Path.GetDirectoryName(item.ContainingProject.FileName);
            if (itemFullPath.StartsWith(projectFullPath) && itemFullPath.Length > projectFullPath.Length)
            {
                int increment = (projectFullPath.EndsWith(@"\")) ? 0 : 1;
                return itemFullPath.Substring(projectFullPath.Length + increment);
            }
            else
            {
                return String.Empty;
            }
        }
    }

    /// <summary>
    /// Implementation supporting VB.Net file and web application projects.
    /// </summary>
    public class WebApplicationVBExtractResourceAction : GenericVBExtractResourceAction
    {
        #region IExtractResourceAction Members

        /// <summary>
        /// Priority of the action. If there are multiple actions supporting the same item, action with
        /// the highest priority will be selected
        /// </summary>
        public override int Priority
        {
            get { return 50; }
        }

        /// <summary>
        /// Gets the default relative path for resource file.
        /// </summary>
        public override string DefaultResourceFilePath
        {
            get
            {
                return "App_GlobalResources";
            }
        }

        /// <summary>
        /// Queries if this action supports the provided project item and its containing project
        /// </summary>
        /// <param name="item">Project item to query support for</param>
        /// <returns></returns>
        public override bool QuerySupportForProject(ProjectItem item)
        {
            if (item == null) return false;
            bool returnValue = base.QuerySupportForProject(item);
            bool extenderFound = false;
            if (returnValue)
            {
                string[] names = (string[])(item.ContainingProject.ExtenderNames);
                foreach (string name in names)
                {
                    if (name.Equals("WebApplication"))
                    {
                        extenderFound = true;
                        break;
                    }
                }
            }
            return returnValue && extenderFound;
        }

        /// <summary>
        /// This method will be used for filtering resource files displayed to user
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        /// <returns>true if resource file is valid and should be displayed to user</returns>
        public override bool IsValidResourceFile(ProjectItem item)
        {
            return base.IsValidResourceFile(item) || WebApplicationCSharpExtractResourceAction.IsValidContentResourceFile(item);
        }

        #endregion

        /// <summary>
        /// Determines the namespace of the provided resource file
        /// </summary>
        /// <param name="file">Reference to the resource file</param>
        /// <returns>Namespace to be used to access the resource file</returns>
        protected override string GetNamespacePrefix(ResourceFile file)
        {
            if (file.CustomToolName.Equals("GlobalResourceProxyGenerator"))
            {
                return "Resources.";
            }
            else
            {
                return base.GetNamespacePrefix(file);
            }
        }

        /// <summary>
        /// This method should update properties on a recently created resource file so that it
        /// is correctly supported by the same instance of IExtractResourceAction
        /// </summary>
        /// <param name="item">Project item for the resource file</param>
        public override void UpdateResourceFileProperties(ProjectItem item)
        {
            if (item != null)
            {
                string relPath = WebApplicationCSharpExtractResourceAction.GetRelativePathForItem(item);
                if (relPath.Equals("App_GlobalResources"))
                {
                    item.Properties.Item("CustomTool").Value = "GlobalResourceProxyGenerator";
                    item.Properties.Item("ItemType").Value = "Content";
                }
                else
                {
                    base.UpdateResourceFileProperties(item);
                }
            }
        }
    }
    
    /// <summary>
    /// Represents a region of the code that is going to be refactored.
    /// </summary>
    /// <remarks>Contains methods to preview changes to be applied and to do the actual refactoring</remarks>
    public class ExtractToResourceActionSite
    {
        private BaseHardCodedString stringInstance;

        private IExtractResourceAction actionObject;

        /// <summary>
        /// Gets the string instance to refactor
        /// </summary>
        public BaseHardCodedString StringToExtract
        {
            get { return stringInstance; }
        }

        /// <summary>
        /// Gets the action object to use for refactoring
        /// </summary>
        public IExtractResourceAction ActionObject
        {
            get { return this.actionObject; }
        }

        /// <summary>
        /// Creates a new instance to refactor provided string instance
        /// </summary>
        /// <param name="stringInstance">String instance to refactor</param>
        public ExtractToResourceActionSite(BaseHardCodedString stringInstance)
        {
            this.stringInstance = stringInstance;
            actionObject = GetExtractResourceActionObject(this.StringToExtract);
        }

        /// <summary>
        /// Returns a preview of the line after the literal is replaced by a reference to the resource
        /// </summary>
        /// <param name="file">Resource file containing the resource</param>
        /// <param name="resourceName">Name of the resource</param>
        /// <returns>Full line where the text would be replaced</returns>
        public string PreviewChanges(ResourceFile file, string resourceName)
        {
            string reference = this.actionObject.GetResourceReference(file, resourceName);
            reference = BaseHardCodedString.GetShortestReference(reference, this.StringToExtract.GetImportedNamespaces());
            return reference;
        }

        /// <summary>
        /// Extracts the string to a resource file and replaces it with a reference to the new entry
        /// </summary>
        /// <param name="file"></param>
        /// <param name="resourceName"></param>
        public void ExtractStringToResource(ResourceFile file, string resourceName)
        {
            string reference = this.actionObject.GetResourceReference(file, resourceName);
            reference = BaseHardCodedString.GetShortestReference(reference, this.StringToExtract.GetImportedNamespaces());
            this.StringToExtract.Replace(reference);
        }

        #region Static Methods

        /// <summary>
        /// A cache of action items used before, key is a ProjectItem instance since a ProjectItem will always
        /// have the same action object
        /// </summary>
        private static Dictionary<ProjectItem, IExtractResourceAction> actionObjectCache = new Dictionary<ProjectItem, IExtractResourceAction>();
        
        /// <summary>
        /// List of available implementation in the current assembly.
        /// </summary>
        private static List<IExtractResourceAction> availableActionObjectList;

        /// <summary>
        /// Gets the correct ExtractResource action implementation for the provided string instance
        /// </summary>
        /// <param name="instance">String instance to refactor</param>
        /// <returns>an IExtractResourceAction implementation. null if no suitable action is found.</returns>
        public static IExtractResourceAction GetExtractResourceActionObject(BaseHardCodedString instance)
        {
            if (actionObjectCache.ContainsKey(instance.Parent)) return actionObjectCache[instance.Parent];
            if (availableActionObjectList == null)
            {
                availableActionObjectList = new List<IExtractResourceAction>();
                Assembly currentAssembly = Assembly.GetExecutingAssembly();
                Type[] types = currentAssembly.GetTypes();
                foreach (Type type in types)
                {
                    if (!type.IsAbstract && (typeof(IExtractResourceAction)).IsAssignableFrom(type))
                    {
                        try
                        {
                            IExtractResourceAction currentAction = System.Activator.CreateInstance(type) as IExtractResourceAction;
                            if (currentAction != null)
                            {
                                availableActionObjectList.Add(currentAction);
                            }
                        }
                        catch (TargetInvocationException e)
                        {
                            System.Diagnostics.Trace.TraceError(e.ToString());
                        }
                        catch (ArgumentException e)
                        {
                            System.Diagnostics.Trace.TraceError(e.ToString());
                        }
                        catch (MissingMethodException e)
                        {
                            System.Diagnostics.Trace.TraceError(e.ToString());
                        }
                    }
                }
            }
            IExtractResourceAction action = null;
            foreach (IExtractResourceAction currentAction in availableActionObjectList)
            {
                if (!currentAction.QuerySupportForProject(instance.Parent))
                {
                    continue;
                }
                if (action == null || currentAction.Priority > action.Priority)
                {
                    action = currentAction;
                }
            }
            if (action != null)
            {
                if (actionObjectCache.Count > 30) actionObjectCache.Clear();
                actionObjectCache.Add(instance.Parent, action);
            }
            return action;
        }
        #endregion
    }

    
}
